﻿
// Homework3View.cpp: CHomework3View 类的实现
//

#include "pch.h"
#include "framework.h"
// SHARED_HANDLERS 可以在实现预览、缩略图和搜索筛选器句柄的
// ATL 项目中进行定义，并允许与该项目共享文档代码。
#ifndef SHARED_HANDLERS
#include "Homework3.h"
#endif

#include "Homework3Doc.h"
#include "Homework3View.h"
#include <functional>
#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// CHomework3View

IMPLEMENT_DYNCREATE(CHomework3View, CView)

BEGIN_MESSAGE_MAP(CHomework3View, CView)
	// 标准打印命令
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CHomework3View::OnFilePrintPreview)
	ON_WM_CONTEXTMENU()
	ON_WM_RBUTTONUP()
	ON_WM_ACTIVATE()
	ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()
// 指定算法选择
#define Cohen_Sutherland 0
// CHomework3View 构造/析构

CHomework3View::CHomework3View() noexcept
	:point1(-1, -1), point2(-1, -1),
	rectangle(100, 100, 300, 200)
{
	// TODO: 在此处添加构造代码

}

CHomework3View::~CHomework3View()
{
}

BOOL CHomework3View::PreCreateWindow(CREATESTRUCT& cs)
{
	// TODO: 在此处通过修改
	//  CREATESTRUCT cs 来修改窗口类或样式

	return CView::PreCreateWindow(cs);
}

// CHomework3View 绘图
void CHomework3View::OnDraw(CDC* pDC)
{
	CHomework3Doc* pDoc = GetDocument();
	ASSERT_VALID(pDoc);
	if (!pDoc)
		return;
	// TODO: 在此处为本机数据添加绘制代码
	if (point1.x < 0 || point2.x < 0)
		pDC->Rectangle(rectangle);
	else
	{
		pDC->MoveTo(point1);
		pDC->LineTo(point2);
		pDC->Rectangle(rectangle);
		//pDC->FillSolidRect(rectangle, RGB(255, 255, 255));
		//pDC->SetBoundsRect();
		CPen pen(PS_SOLID, 1, RGB(255, 0, 0));
		pDC->SelectObject(&pen);
		pDC->MoveTo(point_bound1);
		pDC->LineTo(point_bound2);
	}
}

// CHomework3View 打印


void CHomework3View::OnFilePrintPreview()
{
#ifndef SHARED_HANDLERS
	AFXPrintPreview(this);
#endif
}

BOOL CHomework3View::OnPreparePrinting(CPrintInfo* pInfo)
{
	// 默认准备
	return DoPreparePrinting(pInfo);
}

void CHomework3View::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 添加额外的打印前进行的初始化过程
}

void CHomework3View::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
{
	// TODO: 添加打印后进行的清理过程
}

void CHomework3View::OnRButtonUp(UINT /* nFlags */, CPoint point)
{
	ClientToScreen(&point);
	OnContextMenu(this, point);
}

void CHomework3View::OnContextMenu(CWnd* /* pWnd */, CPoint point)
{
#ifndef SHARED_HANDLERS
	theApp.GetContextMenuManager()->ShowPopupMenu(IDR_POPUP_EDIT, point.x, point.y, this, TRUE);
#endif
}


// CHomework3View 诊断

#ifdef _DEBUG
void CHomework3View::AssertValid() const
{
	CView::AssertValid();
}

void CHomework3View::Dump(CDumpContext& dc) const
{
	CView::Dump(dc);
}

CHomework3Doc* CHomework3View::GetDocument() const // 非调试版本是内联的
{
	ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CHomework3Doc)));
	return (CHomework3Doc*)m_pDocument;
}
#endif //_DEBUG


void CHomework3View::OnLButtonDown(UINT nFlags, CPoint point)
{
	// TODO: 在此添加消息处理程序代码和/或调用默认值
	if (point1.x < 0 || point2.x >= 0)
	{
		Invalidate();
		point1 = point;
		point2.x = -1;
	}
	else
	{
		point2 = point;
		using code_type = std::uint8_t;
		enum class edge_code :code_type
		{
			left = 1,
			right = 1 << 1,
			bottom = 1 << 2,
			top = 1 << 3,
		};
		std::function<code_type(LONG)> left_right_coding(
			[this](LONG coord) {
				code_type code = 0;
				if (coord < rectangle.left)
					code |= static_cast<code_type>(edge_code::left);
				else if (coord > rectangle.right)
					code |= static_cast<code_type>(edge_code::right);
				return code;
			});
		std::function<code_type(LONG)> top_bottom_coding(
			[this](LONG coord) {
				code_type code = 0;
				if (coord < rectangle.top)
					code |= static_cast<code_type>(edge_code::top);
				else if (coord > rectangle.bottom)
					code |= static_cast<code_type>(edge_code::bottom);
				return code;
			});
		std::function<code_type(const CPoint&)> coding(
			[this, left_right_coding, top_bottom_coding](const CPoint& point) {
				return left_right_coding(point.x) | top_bottom_coding(point.y);
			});
		std::function<edge_code(code_type)> edge(
			[](code_type code)
			{
				return static_cast<edge_code>(code ^ (code & (code - 1)));
			});
		point_bound1 = point1;
		point_bound2 = point2;
#if Cohen_Sutherland
		const float k = (point_bound2.y - point_bound1.y) / static_cast<float>(point_bound2.x - point_bound1.x);
		std::function<CPoint(CPoint, CPoint, code_type&)> cross(
			[this, edge, left_right_coding, top_bottom_coding, k](CPoint point1, CPoint point2, code_type& code)->CPoint
			{
				LONG the_other_coord;
				switch (edge(code))
				{
				case edge_code::left:
					the_other_coord = point1.y + k * (rectangle.left - point1.x);
					code = top_bottom_coding(the_other_coord);
					return CPoint(rectangle.left, the_other_coord);
				case edge_code::right:
					the_other_coord = point1.y + k * (rectangle.right - point1.x);
					code = top_bottom_coding(the_other_coord);
					return CPoint(rectangle.right, the_other_coord);
				case edge_code::bottom:
					the_other_coord = point1.x + (rectangle.bottom - point1.y) / k;
					code = left_right_coding(the_other_coord);
					return CPoint(the_other_coord, rectangle.bottom);
				case edge_code::top:
					the_other_coord = point1.x + (rectangle.top - point1.y) / k;
					code = left_right_coding(the_other_coord);
					return CPoint(the_other_coord, rectangle.top);
				}
			});
		//static_cast<code_type>(edge_code::left);
		for (code_type code1 = coding(point_bound1), code2 = coding(point_bound2);code1 | code2;)
		{
			if (code1 & code2)
			{
				point_bound1.x = point_bound2.x = -1;
				break;
			}
			code1 ?
				point_bound1 = cross(point_bound1, point_bound2, code1) :
				point_bound2 = cross(point_bound2, point_bound1, code2);
		}
#else
		std::function<CPoint(CPoint, CPoint, code_type, code_type)>bound_with_inside_and_outside(
			[coding, edge, this](CPoint point1, CPoint point2, code_type code1, code_type code2)->CPoint
			{
				CPoint middle(point1.x + point2.x >> 1, point1.y + point2.y >> 1);
				std::function<bool()> not_on_bound;
				switch (edge(code1 ? code1 : code2))
				{
				case edge_code::left:
					not_on_bound = [&middle, this]()
					{
						return rectangle.left != middle.x;
					};
					break;
				case edge_code::right:
					not_on_bound = [&middle, this]()
					{
						return rectangle.right != middle.x;
					};
					break;
				case edge_code::bottom:
					not_on_bound = [&middle, this]()
					{
						return rectangle.bottom != middle.y;
					};
					break;
				case edge_code::top:
					not_on_bound = [&middle, this]()
					{
						return rectangle.top != middle.y;
					};
					break;
				}
				for (;not_on_bound();middle.x = point1.x + point2.x >> 1, middle.y = point1.y + point2.y >> 1)
				{
					code_type code_middle = coding(middle);
					if (static_cast<bool>(code_middle) == static_cast<bool>(code1))
					{
						if (middle == point1)
							return point2;
						point1 = middle;
						code1 = code_middle;
					}
					else if (middle == point2)
						return point1;
					else
					{
						point2 = middle;
						code2 = code_middle;
					}
				}
				return middle;
			});
		while (true)
		{
			code_type code1 = coding(point_bound1);
			code_type code2 = coding(point_bound2);
			if (code1 & code2)
			{
				point_bound1.x = point_bound2.x = -1;
				break;
			}
			if ((code1 | code2) == 0)
				break;
			//if ((code1||code2)&&!(code1&&code2))
			if (static_cast<bool>(code1) != static_cast<bool>(code2))
			{
				point_bound1 = bound_with_inside_and_outside(point_bound1, point_bound2, code1, code2);
				point_bound2 = code1 ? point2 : point1;
				//(code1 ? p1 : p2) = bound_with_inside_and_outside(p1, p2, code1, code2);
				break;
			}
			CPoint middle(point_bound1.x + point_bound2.x >> 1, point_bound1.y + point_bound2.y >> 1);
			code_type code_middle = coding(middle);
			if (code_middle)
			{
				code_type part_middle = code_middle & static_cast<code_type>(3);
				code_type part1 = code1 & static_cast<code_type>(3);
				code_type part2 = code2 & static_cast<code_type>(3);
				if (part1 == part_middle && part2 != part_middle)
					point_bound1 = middle;
				else if (part1 != part_middle && part2 == part_middle)
					point_bound2 = middle;
				else
				{
					part_middle = code_middle & static_cast<code_type>(12);
					part1 = code1 & static_cast<code_type>(12);
					part2 = code2 & static_cast<code_type>(12);
					if (part1 == part_middle && part2 != part_middle)
						point_bound1 = middle;
					else if (part1 != part_middle && part2 == part_middle)
						point_bound2 = middle;
				}
			}
			else
			{
				point_bound1 = bound_with_inside_and_outside(point_bound1, middle, code1, code_middle);
				point_bound2 = bound_with_inside_and_outside(point_bound2, middle, code2, code_middle);
				break;
			}
		}
#endif
		OnDraw(&CClientDC(this));
	}	
	CView::OnLButtonDown(nFlags, point);
}
#undef Cohen_Sutherland
